package service

import (
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"golang.org/x/sys/windows/registry"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
)

var VapServ = new(vapService)

type vapService struct {
}

const (
	registryPathForRdpPort      = "SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp"
	registryPathForRdpStartPath = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\TSAppAllowList"
	registryPathForRdpAutoOld   = "SYSTEM\\CurrentControlSet\\Control\\Terminal Server"
	registryPathForRdpNetAuth   = "SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp"
	registryPathForRdpCpu       = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Executive"
)

type VapHostConfig struct {
	AppHost struct {
		Uid     string `yaml:"uid"`
		RdpIp   string `yaml:"rdpIp"`
		RdpPort int    `yaml:"rdpPort"`
	} `yaml:"appHost"`
	Tcp struct {
		Servers []string `yaml:"servers"`
	} `yaml:"tcp"`
}

type HostConfigForm struct {
	VapHostFolder string `json:"vapHostFolder,omitempty"`
	Uid           string `json:"uid,omitempty"`
	RdpServer     string `json:"rdpServer,omitempty"`
	TcpServer     string `json:"tcpServer,omitempty"`
}

func (s vapService) GetVapHostConfig(fp string) (out HostConfigForm, err error) {
	var (
		conf VapHostConfig
	)

	v := viper.New()
	v.SetConfigName("config_vap_host")
	v.SetConfigType("yaml")
	v.AddConfigPath(fp)
	err = v.ReadInConfig()
	if err != nil {
		return
	}
	err = v.Unmarshal(&conf)
	if err != nil {
		return
	}
	out.VapHostFolder = fp
	out.Uid = conf.AppHost.Uid
	out.RdpServer = fmt.Sprintf("%s:%d", conf.AppHost.RdpIp, conf.AppHost.RdpPort)
	out.TcpServer = conf.Tcp.Servers[0]
	return out, nil
}

func (s vapService) SetVapHostConfig(in HostConfigForm) (err error) {
	v := viper.New()
	v.SetConfigName("config_vap_host")
	v.SetConfigType("yaml")
	v.AddConfigPath(in.VapHostFolder)
	err = v.ReadInConfig()
	if err != nil {
		return
	}
	conf := new(VapHostConfig)
	err = v.Unmarshal(conf)
	if err != nil {
		return
	}
	arr := strings.Split(in.RdpServer, ":")
	if len(arr) != 2 {
		err = errors.New(" 无效的 rdpServer")
		logrus.Error(err)
		return
	}
	port, err := strconv.ParseInt(arr[1], 10, 64)
	if err != nil {
		err = errors.New(fmt.Sprintf(" 无效的端口信息:%s", err))
		logrus.Error(err)
		return
	}
	v.Set("apphost.uid", in.Uid)
	v.Set("apphost.rdpip", arr[0])
	v.Set("apphost.rdpport", int(port))
	v.Set("tcp.servers", []string{in.TcpServer})
	err = v.WriteConfig()
	if err != nil {
		logrus.Error(err)
		return
	}
	// 注册并启动服务
	cmd := exec.Command(filepath.Join(in.VapHostFolder, "miao_vap_host.exe"), "install")
	_ = cmd.Run()
	cmd = exec.Command(filepath.Join(in.VapHostFolder, "miao_vap_host.exe"), "start")
	_ = cmd.Run()
	return
}

// GetRdpPort 获取 RDP端口
func (s vapService) GetRdpPort() (port int, err error) {
	var (
		key  registry.Key
		pval uint64
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpPort, registry.ALL_ACCESS); err != nil {
		return
	}
	defer key.Close()
	if pval, _, err = key.GetIntegerValue("PortNumber"); err != nil {
		return
	}
	port = int(pval)
	return port, err
}
func (s vapService) SetRdpPort(port int) (err error) {
	var (
		key registry.Key
	)
	key, err = registry.OpenKey(
		registry.LOCAL_MACHINE,
		registryPathForRdpPort,
		registry.ALL_ACCESS,
	)
	if err != nil {
		return
	}
	defer key.Close()
	logrus.Infof("start  SetRdpPort port:%d", port)
	if err = key.SetDWordValue("PortNumber", uint32(port)); err != nil {
		err = errors.New(fmt.Sprintf("设置rdp端口失败 error: %v", err))
		return
	}
	logrus.Infof("end SetRdpPort port:%d", port)
	return
}

// GetConfigForStartType 获取和设置 程序是否可以基于绝对路径启动
func (s vapService) GetConfigForStartType() (b bool, err error) {
	var (
		key   registry.Key
		value uint64
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpStartPath, registry.ALL_ACCESS); err != nil {
		return false, err
	}
	defer key.Close()
	value, _, err = key.GetIntegerValue("fDisabledAllowList")
	if err != nil {
		return
	}
	return value > 0, nil
}

func (s vapService) SetConfigForStartType(b bool) (err error) {
	var (
		key registry.Key
	)
	if key, err = registry.OpenKey(
		registry.LOCAL_MACHINE,
		registryPathForRdpStartPath,
		registry.ALL_ACCESS,
	); err != nil {
		return
	}
	defer key.Close()
	value := uint32(0)
	if b {
		value = 1
	}
	if err = key.SetDWordValue("fDisabledAllowList", value); err != nil {
		return err
	}
	return nil
}

// GetConfigForOldRdp 获取和设置 rdp是否允许向旧版本兼容(兼容 server 2008 R2 或更早的版本)
func (s vapService) GetConfigForOldRdp() (b bool, err error) {
	var (
		key   registry.Key
		value uint64
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpAutoOld, registry.ALL_ACCESS); err != nil {
		return false, err
	}
	defer key.Close()
	value, _, err = key.GetIntegerValue("HonorLegacySettings")
	if err != nil {
		return
	}
	return value > 0, nil
}

func (s vapService) SetConfigForOldRdp(b bool) (err error) {
	var (
		key registry.Key
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpStartPath, registry.ALL_ACCESS); err != nil {
		return
	}
	defer key.Close()
	value := uint32(0)
	if b {
		value = 1
	}
	if err = key.SetDWordValue("HonorLegacySettings", value); err != nil {
		return err
	}
	return nil
}

// GetConfigForNetAuth 获取和设置不使用网络级别的身份验证, true-不使用，false-使用网络级别的身份认证
func (s vapService) GetConfigForNetAuth() (b bool, err error) {
	var (
		key   registry.Key
		value uint64
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpNetAuth, registry.ALL_ACCESS); err != nil {
		return false, err
	}
	defer key.Close()
	value, _, err = key.GetIntegerValue("UserAuthentication")
	// 0-不使用网络级别的身份认证， 1- 使用网络级别的身份认证
	if err != nil {
		return
	}
	return value == 0, nil
}

func (s vapService) SetConfigForNetAuth(b bool) (err error) {
	var (
		key registry.Key
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpNetAuth, registry.ALL_ACCESS); err != nil {
		return
	}
	defer key.Close()
	value := uint32(0)
	if b {
		value = 1
	}
	if err = key.SetDWordValue("UserAuthentication", value); err != nil {
		return err
	}
	return nil
}

// GetConfigForCpu 获取和设置平均分配CPU调度,0-公平分配
func (s vapService) GetConfigForCpu() (b bool, err error) {
	var (
		key   registry.Key
		value uint64
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpCpu, registry.ALL_ACCESS); err != nil {
		return false, err
	}
	defer key.Close()
	value, _, err = key.GetIntegerValue("AdditionalCriticalWorkerThreads")
	if err != nil {
		return
	}
	return value == 0, nil
}
func (s vapService) SetConfigForCpu(b bool) (err error) {
	var (
		key registry.Key
	)
	if key, err = registry.OpenKey(registry.LOCAL_MACHINE, registryPathForRdpCpu, registry.ALL_ACCESS); err != nil {
		return
	}
	defer key.Close()
	value := uint32(1)
	if b {
		value = 0
	}
	if err = key.SetDWordValue("AdditionalCriticalWorkerThreads", value); err != nil {
		return err
	}
	return nil
}

// Reboot 重启主机
func (s vapService) Reboot() (err error) {
	// 使用 shutdown 命令（需管理员权限）
	cmd := exec.Command("shutdown", "/r", "/f", "/t", "0")

	// 设置隐藏命令行窗口（可选）
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}

	// 执行命令
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("重启失败: %v\n请以管理员身份运行程序", err)
	}
	return nil
}

// WrapCrack 调用 wrap 执行rdp破解
func (s vapService) WrapCrack() (err error) {
	execPath, err := os.Executable()
	if err != nil {
		return
	}
	cmd := exec.Command(filepath.Join(filepath.Dir(execPath), "rdpwrap", "RDPWInst.exe"), "-i")
	// 执行命令
	if err = cmd.Run(); err != nil {
		return fmt.Errorf("调用 wrap 时发生错误：%s", err)
	}
	return nil
}

const (
	regForShowDisk = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"
	noDrivesVal    = 0x000000ff //  0x000000ff （A - H）； 0x03ffffff （A - Z）；
)

// GetHideDiskStatus true-隐藏；false-不隐藏
func (s vapService) GetHideDiskStatus() (b bool, err error) {
	var (
		key registry.Key
	)
	key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, regForShowDisk, registry.ALL_ACCESS)
	if err != nil {
		return
	}
	defer key.Close()
	buf := make([]byte, 1024)
	_, _, err = key.GetValue("NoDrives", buf)
	if err != nil {
		b = false
	} else {
		b = true
	}
	err = nil
	return
}

// SetHideDisk true-隐藏；false-不隐藏
func (s vapService) SetHideDisk(b bool) (err error) {
	var (
		key registry.Key
	)
	key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, regForShowDisk, registry.ALL_ACCESS)
	if err != nil {
		return
	}
	defer key.Close()
	if b {
		// 设置 NoDrives,控制盘符是否可见，未设置的盘符不会显示在资源管理器中，但是直接输入路径还是可以访问的。
		if err = key.SetDWordValue("NoDrives", noDrivesVal); err != nil {
			return fmt.Errorf("设置 NoDrives 失败: %v", err)
		}
	} else {
		if err = key.DeleteValue("NoDrives"); err != nil {
			return fmt.Errorf("删除 NoDrives 失败: %v", err)
		}
	}
	return
}

// GetDisabledDiskStatus true-隐藏；false-不隐藏
func (s vapService) GetDisabledDiskStatus() (b bool, err error) {
	var (
		key registry.Key
	)
	key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, regForShowDisk, registry.ALL_ACCESS)
	if err != nil {
		return
	}
	defer key.Close()
	buf := make([]byte, 1024)
	_, _, err = key.GetValue("NoViewOnDrive", buf)
	if err != nil {
		b = false
	} else {
		b = true
	}
	err = nil
	return
}

// SetDisabledDiskStatus true-隐藏；false-不隐藏
func (s vapService) SetDisabledDiskStatus(b bool) (err error) {
	var (
		key registry.Key
	)
	key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, regForShowDisk, registry.ALL_ACCESS)
	if err != nil {
		return
	}
	defer key.Close()
	if b {
		// 设置 NoDrives,控制盘符是否可见，未设置的盘符不会显示在资源管理器中，但是直接输入路径还是可以访问的。
		if err = key.SetDWordValue("NoViewOnDrive", noDrivesVal); err != nil {
			return fmt.Errorf("设置 NoViewOnDrive 失败: %v", err)
		}
	} else {
		if err = key.DeleteValue("NoViewOnDrive"); err != nil {
			return fmt.Errorf("删除 NoViewOnDrive 失败: %v", err)
		}
	}
	return
}

// KillExplorer 杀死或者拉起 explorer
func (s vapService) KillExplorer() (err error) {
	cmdKill := exec.Command("taskkill", "/f", "/im", "explorer.exe")
	cmdKill.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	if err = cmdKill.Run(); err != nil {
		return fmt.Errorf("终止资源管理器失败: %v", err)
	}
	return
}

func (s vapService) StartExplorer() (err error) {
	cmdStart := exec.Command("explorer.exe")
	cmdStart.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	return cmdStart.Run()
}
